#include "precomp.h"
#include "EntityBuilding.h"
#include "../Entity.h"
#include "../../Map/Grid.h"

namespace Tmpl {

#define BUILDING_JOBS 8

static DataValuePool<EntityBuilding> gBuildings;

// EntitUnityBuildingystem setup functions
void EntityBuildingSystem::Init()
{
	gBuildings.Init();
}
void EntityBuildingSystem::Clear()
{
	gBuildings.Clear();
}
int EntityBuildingSystem::NewBuilding(Entity* owner, Vec2f pos, const UnitData& data)
{
	int id = gBuildings.NewValue();

	EntityBuilding* value = gBuildings.GetValue(id);
	value->mOwner = owner;
	value->mPos = pos;
	value->mHP = value->mHPMax = data.building.hp;
	value->mCollisionSizeX = value->mCollisionSizeY = 0;
	value->mConditionFlags = EntityBuilding::BUILDING_CONDITION_ALIVE;

	// Set the collision
	Empire* empire = EmpireSystem::GetEmpire(value->mOwner->GetEmpire());
	empire->ExecCollisionFunc(data.building.collisionFunc, id);
	
	// Add it to grid
	int posX = int(pos.x);
	int posY = int(pos.y);
	int entId = value->mOwner->GetId();
	for (int y=0; y<value->mCollisionSizeY; ++y)
	for (int x=0; x<value->mCollisionSizeX; ++x)
	{
		int cellId = Grid::GetCellIdFromXY(posX+x, posY+y);
		Grid::SetCollisionId(cellId, entId);
	}

	return id;
}
void EntityBuildingSystem::RemoveBuilding(int id)
{
	gBuildings.RemoveValue(id);
}
EntityBuilding* EntityBuildingSystem::GetBuilding(int id)
{
	return gBuildings.GetValue(id);
}
// EntityBuilding
void EntityBuilding::Kill()
{
	// Remove it from grid
	int posX = int(mPos.x);
	int posY = int(mPos.y);
	int entId = mOwner->GetId();
	for (int y=0; y<mCollisionSizeY; ++y)
	for (int x=0; x<mCollisionSizeX; ++x)
	{
		int cellId = Grid::GetCellIdFromXY(posX+x, posY+y);
		Grid::SetCollisionId(cellId, Grid::COLLISION_NONE);
	}
	mHP = 0.0f;
	ConditionRemoveFlag(BUILDING_CONDITION_ALIVE);
	ConditionAddFlag(BUILDING_CONDITION_DEAD);
}
void EntityBuilding::SetCollisionSize(char x, char y)
{ 
	mCollisionSizeX = x; 
	mCollisionSizeY = y; 
	mCollision = MALLOC64(x*y, bool);
	memset(mCollision, 0, x*y*sizeof(bool));
}
void EntityBuilding::AddHP(float hp, int entitySource)
{
	mHP += hp;

	// Kill unit if hp drops below 0
	if (mHP < 0.0f && ConditionHasFlag(BUILDING_CONDITION_ALIVE))
	{
		ConditionRemoveFlag(BUILDING_CONDITION_ALIVE);
		ConditionAddFlag(BUILDING_CONDITION_DEAD);

		// perhaps send a message to the killer?
	}

	// We finished building if it reaches 100% hp
	if (mHP >= mHPMax)
	{
		ConditionAddFlag(BUILDING_CONDITION_FINISHED);
	}

	// Clamp it
	mHP = Clamp(0.0f, mHPMax, mHP);
}
// Updating
void EntityBuildingSystem::UpdateBuildingChunk(int chunk)
{
	int count = gBuildings.GetValueCount();
	int step = count / BUILDING_JOBS;
	int from = step * chunk;
	int to = ((chunk==BUILDING_JOBS-1) ? count : step * (chunk+1));
	for (int i=from; i<to; ++i)
	{
		EntityBuilding* v = gBuildings.GetUnmappedValue(i);
		Entity* ent = v->mOwner;

		if (v->mHP <= 0.0f)
		{
			v->Kill();
			ent->Kill();
		}
	}
}
JobHandle EntityBuildingSystem::Update(JobHandle dependency)
{
	JobHandle buildingUpdate = JobManager::NewJob();
	for (int i=0; i<BUILDING_JOBS; ++i)
	{
		JobHandle job = JobManager::NewJob(&EntityBuildingSystem::UpdateBuildingChunk,i);
		JobManager::AddJobDependency(job, dependency);
		JobManager::AddExecuteJob(job);
		JobManager::AddJobDependency(buildingUpdate, job);
	}
	JobManager::AddExecuteJob(buildingUpdate);
	return buildingUpdate;
}
}